/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { pickedLineData, pickedVertexData } from '@amcax/base';
import {
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { CurvedBaseCommand, CurvedCmdDialogParams } from '@app-cad/types';
import { RJSFSchema } from '@rjsf/utils';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';
import { useEffect, useState } from 'react';
import './index.scss';

type Params = {
  points: {
    point: string;
    vector: string;
    tension: number;
  }[];
};

type FormData = {
  point?: pickedVertexData;
  direction?: pickedLineData;
  tension: number;
}[];

function CmdSpline({ title, onClose, props }: CurvedCmdDialogParams) {
  const defaultForm = [
    { point: undefined, tension: 1 },
    { point: undefined, tension: 1 },
  ];

  const [formData, setFormData] = useState<FormData>(defaultForm);

  const { picker } = useTools();

  useEffect(() => {
    preview();
  }, [formData]);

  useAsyncEffect(async () => {
    if (props == null) {
      setFormData(defaultForm);
      return;
    }

    const params = props.params as Params;

    await initEditDependency([
      ...params.points.map((p) => p.point),
      ...params.points.map((p) => p.vector),
    ]);

    const formData: FormData = params.points.map((p) => ({
      point: picker.generatePickObjByPickID(p.point) as pickedVertexData,
      direction: p.vector
        ? (picker.generatePickObjByPickID(p.vector) as pickedLineData)
        : undefined,
      tension: p.tension,
    }));
    formData.push({ point: undefined, tension: 1 });
    setFormData(formData);
  }, [props?.shape]);

  const checkAndFinishDraw = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute(false);

    onClose();
  };

  const { initEditDependency, initingEdit, clear, confirmCommand, isLoading } =
    useCommand('cmd-spline', {
      commandName: 'CreateCurve',
      editDxid: props?.shape,
      onClose,
      finishDraw: checkAndFinishDraw,
    });

  useEffect(() => {
    if (!isFormDataValid()) {
      clear();
    }
  }, [formData]);

  const nonNullItems = formData.filter((p) => p?.point != null);

  const execute = (preview: boolean) => {
    const points = nonNullItems.map((p) => ({
      point: p.point.vertexId,
      vector: p.direction?.edgeId,
      tension: p.tension,
    }));

    return confirmCommand(
      {
        type: 'Bspline',
        a: {
          mode: 'Interpolate',
          points,
        },
      },
      preview,
    );
  };

  const isFormDataValid = () => {
    return (
      nonNullItems.length > 1 &&
      formData.slice(0, -1).every((p) => p?.point != null)
    );
  };

  const preview = async () => {
    clear();
    if (!isFormDataValid()) {
      return;
    }

    await execute(true);
  };

  const schema: RJSFSchema = {
    type: 'array',
    items: {
      type: 'object',
      title: '通过点',
      properties: {
        point: {
          type: 'string',
          title: '通过点',
          format: PickInputRJSFFormat.PickVertex,
        },
        direction: {
          type: 'string',
          title: '切线方向',
          format: PickInputRJSFFormat.PickLine,
        },
        tension: {
          type: 'number',
          title: '张度',
          default: 1,
          format: NumberInputFormat.Real,
        },
      },
    },
  };

  return (
    <PickableCommandForm
      disabled={initingEdit}
      onClose={onClose}
      title={title}
      schema={schema}
      submitLoading={isLoading}
      formData={formData}
      onChange={(v) => {
        const newFormData = [...v];
        if (newFormData[newFormData.length - 1]?.point != null) {
          newFormData.push({ point: undefined, tension: 1 });
        }

        setFormData(newFormData);
      }}
      submitText='完成'
      onSubmit={checkAndFinishDraw}
      extraUiSchema={{
        'ui:options': {
          classNames:
            'command-form command-form-array command-form_cmd-cad-spline',
        },
        items: {
          point: {
            'ui:placeholder': '请输入通过点',
            'ui:autofocus': true,
          },
          direction: {
            'ui:placeholder': '请输入方向',
            'ui:options': {
              canBeAutoFocused: false,
              showArrow: true,
            },
          },
          tension: { 'ui:placeholder': '请输入张度' },
        },
      }}
      isFormDataValid={isFormDataValid()}
    />
  );
}

export const cmdSpline: CurvedBaseCommand = {
  id: 'CmdSpline',
  label: '样条曲线',
  icon: 'commands/curved/spline',
  help: {
    remark: 'tips.redo',
  },
  create: (label, onClose, props) => (
    <CmdSpline
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
